package com.xzm.utils;

import lombok.Data;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Predicate;

/**
 * @author 瞎琢磨先生 https://github.com/xiazuomo/xzm-utils.git
 * @version 1.0
 * @date 2022/7/23 23:38
 * 原作者：https://gitee.com/xiazuomo/bilibili/blob/master/damowang/src/main/java/cn/xiaoke/util/ListToTreeUtil.java#
 */
@Slf4j
public class ListToTreeNormal {

    public static void main(String[] args) {
        final List<Node> nodeList = getList();

        // final List<Node> tree = convertListToTree(nodeList, (t) -> t.getPId() == 0);
        final List<Node> tree = convertListToTreeNew(nodeList, Node::getId, Node::getPId, Node::getChild, Node::setChild, (t) -> t.getPId() == 0);
        log.info("convertListToTree：{}", JsonUtil.toJson(tree));

        List<Node> endList = new ArrayList<>();
        for (Node node : tree) {
            getEndNodeList(node, endList);
        }
        log.info("endList：{}", JsonUtil.toJson(endList));

        filterTreeByKeyword(tree, "1");
        log.info("模糊查询的 tree：[{}]", JsonUtil.toJson(tree));

    }

    /**
     * 模糊搜索关键字(递归方法)
     * https://blog.csdn.net/xl_1803/article/details/123028218?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-123028218-blog-124882516.pc_relevant_multi_platform_whitelistv6&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-123028218-blog-124882516.pc_relevant_multi_platform_whitelistv6&utm_relevant_index=1
     *
     * @param nodeTree 任意层级的 tree
     * @param keyword  模糊查询的关键字
     */
    public static void filterTreeByKeyword(List<Node> nodeTree, String keyword) {
        Iterator<Node> inter = nodeTree.iterator();
        while (inter.hasNext()) {
            // 获取当前遍历到的目录
            Node node = inter.next();
            // 如果当前目录名称包含关键字，则什么也不做（不移除），否则就看下一级
            if (!node.getName().contains(keyword)) {
                // 取出下一级目录集合
                List<Node> childrenCategoryList = node.getChild();
                // 递归
                if (CollectionUtils.isNotEmpty(childrenCategoryList)) {
                    filterTreeByKeyword(childrenCategoryList, keyword);
                }
                // 下一级目录看完了，如果下一级目录全部被移除，则移除当前目录
                if (CollectionUtils.isEmpty(node.getChild())) {
                    inter.remove();
                }
            }
        }
    }

    /**
     * 获取最末端的子节点
     *
     * @param node        节点
     * @param endNodeList 最末端的子节点
     */
    public static List<Node> getEndNodeList(Node node, List<Node> endNodeList) {
        List<Node> childrenList = node.getChild();
        // 退出递归的条件 只有一层维度结构
        if (childrenList == null || childrenList.size() <= 0) {
            endNodeList.add(node);
        } else {
            // 有多层维度结果
            for (Node n : childrenList) {
                getEndNodeList(n, endNodeList);
            }
        }
        return endNodeList;
    }

    /**
     * 将 list 结构数据转成 tree 结构
     *
     * @param nodeList            list 结构的数据
     * @param isRootNodeCondition pid 为根节点的条件。eg. (t) -> t == 0
     * @return List<Node>
     */
    public static <T> List<T> convertListToTreeNew(List<T> nodeList,
                                                   Function<T, ?> idFun,
                                                   Function<T, ?> pidFun,
                                                   Function<T, List<T>> getChildFun,
                                                   BiConsumer<T, List<T>> setChildCon,
                                                   Predicate<T> isRootNodeCondition) {
        // List<Node> treeList = new ArrayList<>();
        List<T> treeList = new ArrayList<>();
        // Map<Integer, Node> map = new ConcurrentHashMap<>();
        Map<Object, T> map = new ConcurrentHashMap<>();

        // 1 获取所有父节点
        // for (Node node : nodeList) {
        for (T node : nodeList) {
            // if (node.getPId() == 0) {
            if (isRootNodeCondition.test(node)) {
                treeList.add(node);
            }
            // map.put(node.getId(), node);
            map.put(idFun.apply(node), node);
        }
        log.info("获取所有父节点 treeList：[{}]", JsonUtil.toJson(treeList));

        // 2 获取某个节点的父节点，然后将其添加到其父节点的子节点列表中
        // for (Node node : nodeList) {
        for (T node : nodeList) {
            // final Node pNode = map.get(node.getPId());
            final T pNode = map.get(pidFun.apply(node));
            if (pNode != null) {
                // final List<Node> childList = pNode.getChild();
                final List<T> childList = getChildFun.apply(pNode);
                if (childList == null) {
                    // pNode.setChild(new ArrayList<>());
                    setChildCon.accept(pNode, new ArrayList<>());
                }
                // pNode.getChild().add(node);
                getChildFun.apply(pNode).add(node);
            }
            // final List<Node> child = node.getChild();
            final List<T> child = getChildFun.apply(node);
            if (child == null) {
                // node.setChild(new ArrayList<>());
                setChildCon.accept(node, new ArrayList<>());
            }
        }
        log.info("组装完成的 tree 结构：[{}]", JsonUtil.toJson(treeList));
        return treeList;
    }

    public static List<Node> convertListToTree(List<Node> nodeList, Predicate<Node> predicate) {
        List<Node> treeList = new ArrayList<>();
        Map<Integer, Node> map = new ConcurrentHashMap<>();

        // 1 获取所有父节点
        for (Node node : nodeList) {
            // if (node.getPId() == 0) {
            if (predicate.test(node)) {
                treeList.add(node);
            }
            map.put(node.getId(), node);
        }
        log.info("获取所有父节点 treeList：[{}]", JsonUtil.toJson(treeList));

        // 2 获取某个节点的父节点，然后将其添加到其父节点的子节点列表中
        for (Node node : nodeList) {
            final Node pNode = map.get(node.getPId());
            if (pNode != null) {
                final List<Node> childList = pNode.getChild();
                if (childList == null) {
                    pNode.setChild(new ArrayList<>());
                }
                pNode.getChild().add(node);
            }
            final List<Node> child = node.getChild();
            if (child == null) {
                node.setChild(new ArrayList<>());
            }
        }
        log.info("组装完成的 tree 结构：[{}]", JsonUtil.toJson(treeList));
        return treeList;
    }

    /**
     * 造数据
     */
    private static List<Node> getList() {
        List<Node> nodeList = ListUtils.newList(

                // 1 -> 2(4(5)),3
                new Node().setId(1).setPId(0).setName("0-1").setNodeType("dir"),
                new Node().setId(2).setPId(1).setName("1-2").setNodeType("dir"),
                new Node().setId(3).setPId(1).setName("1-3").setNodeType("file"),

                // 2->4,5
                new Node().setId(4).setPId(2).setName("2-4").setNodeType("dir"),
                new Node().setId(5).setPId(2).setName("5-5").setNodeType("file"),
                // 6
                new Node().setId(6).setPId(0).setName("0-6").setNodeType("dir")

        );
        log.info("nodeList：[{}]", JsonUtil.toJson(nodeList));
        return nodeList;
    }

    @Data
    @Accessors(chain = true)
    public static class Node {

        private Integer id;
        private String name;

        private Integer pId;
        private List<Node> child;

        private String nodeType;

    }
}
